package cn.edu.dgut.css.sai.security.oauth2.client.endpoint;

import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationCodeTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequestEntityConverter;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.util.UriComponentsBuilder;

import java.net.URI;
import java.util.Collections;

/**
 * Gitee OAuth2 登录时使用的 OAuth2AccessTokenResponseClient
 *
 * @author sai
 * @since 2.2
 */
final class SaiGiteeOAuth2AccessTokenResponseClient implements OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> {

    @Override
    public OAuth2AccessTokenResponse getTokenResponse(OAuth2AuthorizationCodeGrantRequest authorizationGrantRequest) {
        DefaultAuthorizationCodeTokenResponseClient giteeTokenResponseClient = new DefaultAuthorizationCodeTokenResponseClient();
        giteeTokenResponseClient.setRequestEntityConverter(this::createGiteeOAuth2AccessTokenRequestEntityConverter);
        return giteeTokenResponseClient.getTokenResponse(authorizationGrantRequest);
    }

    /**
     * @see DefaultAuthorizationCodeTokenResponseClient
     * @see OAuth2AuthorizationCodeGrantRequestEntityConverter
     */
    private RequestEntity<?> createGiteeOAuth2AccessTokenRequestEntityConverter(OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest) {
        ClientRegistration clientRegistration = authorizationCodeGrantRequest.getClientRegistration();
        MultiValueMap<String, String> queryParameters = buildQueryParameters(authorizationCodeGrantRequest);
        URI uri = UriComponentsBuilder
                .fromUriString(clientRegistration.getProviderDetails().getTokenUri())
//                        .queryParams(queryParameters)
                .build()
                .toUri();
//            下面两句效果是一样。无论请求参数在url后面拼接，还是放在请求的body部分，码云Api都可以读取。
//            return RequestEntity.post(uri).headers(getDefaultTokenRequestHeaders()).build();
        return RequestEntity.post(uri).headers(getDefaultTokenRequestHeaders()).body(queryParameters);// post请求
    }

    private MultiValueMap<String, String> buildQueryParameters(OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest) {
        ClientRegistration clientRegistration = authorizationCodeGrantRequest.getClientRegistration();
        OAuth2AuthorizationExchange authorizationExchange = authorizationCodeGrantRequest.getAuthorizationExchange();
        MultiValueMap<String, String> formParameters = new LinkedMultiValueMap<>();
        // 都是必填的参数
        formParameters.add(OAuth2ParameterNames.GRANT_TYPE, authorizationCodeGrantRequest.getGrantType().getValue());
        formParameters.add(OAuth2ParameterNames.CODE, authorizationExchange.getAuthorizationResponse().getCode());
        formParameters.add(OAuth2ParameterNames.REDIRECT_URI, authorizationExchange.getAuthorizationRequest().getRedirectUri());
        formParameters.add(OAuth2ParameterNames.CLIENT_ID, clientRegistration.getClientId());
        formParameters.add(OAuth2ParameterNames.CLIENT_SECRET, clientRegistration.getClientSecret());
        return formParameters;
    }

    /**
     * 码云的所有 openAPI 请求的header如果没有User-Agent会报错。
     * 码云官方issues: https://gitee.com/oschina/git-osc/issues/IDBSA
     */
    private HttpHeaders getDefaultTokenRequestHeaders() {
        HttpHeaders headers = new HttpHeaders();
        headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
        // 模拟构造一个User-Agent的header,发挥你的想象。
        headers.put("User-Agent", Collections.singletonList("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36"));
        return headers;
    }
}
